/**
 ******************************************************************************
 * @file    axi_to_jtag_lm.c
 * @author  KEN
 * @version V2.0
 * @date	Jul. 2nd, 2020
 * @brief	AXI-to-JTAG LM Functions Implements
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2020 K'sP</center></h2>
 ******************************************************************************
 */
/* Includes ------------------------------------------------------------------*/
#include "axi_to_jtag_lm.h"
#include "xparameters.h"

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/
#define REG_CR_ENABLE_OFFSET					(0UL)
#define REG_CR_ENABLE_MASK						(1UL<<REG_CR_ENABLE_OFFSET)

#define REG_CR_INT_EN_OFFSET					(1UL)
#define REG_CR_INT_EN_MASK						(1UL<<REG_CR_INT_EN_OFFSET)

#define REG_CR_INT_STATUS_OFFSET				(2UL)
#define REG_CR_INT_STATUS_MASK					(1UL<<REG_CR_INT_STATUS_OFFSET)
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
extern AXI_TO_JTAG_LM_Config AXI_TO_JTAG_LM_ConfigTable[XPAR_AXI_TO_JTAG_LM_NUM_INSTANCES];
/* Private functions ---------------------------------------------------------*/

/* Public functions ----------------------------------------------------------*/
/*************************************************************
 * @brief AXI-to-JTAG LM open, search for matched dev
 *
 * @param hjtaglm : AXI JTAG LM Handler
 * @param dev_id : Device Instance ID
 *
 * @retval : 0 for success, else for error code
 ************************************************************/
int AxiJtagLM_Open(AxiJtagLM_HandleTypeDef *hjtaglm, uint32_t dev_id)
{
	int ret = 0;

	AXI_TO_JTAG_LM_CfgTypeDef *ConfigPtr = NULL;
	uint32_t i;

	if ((hjtaglm == NULL) || (dev_id >= XPAR_AXI_TO_JTAG_LM_NUM_INSTANCES))
	{
		ret = -1;
		goto DONE;
	}

	for (i = 0; i < XPAR_AXI_TO_JTAG_LM_NUM_INSTANCES; i++)
	{
		if (AXI_TO_JTAG_LM_ConfigTable[i].DevID == dev_id)
		{
			ConfigPtr = &AXI_TO_JTAG_LM_ConfigTable[i];
			break;
		}
	}

	if (ConfigPtr == NULL)
	{
		ret = -2;
		goto DONE;
	}

	hjtaglm->Config = ConfigPtr;
	hjtaglm->Instance = (AXI_TO_JTAG_LM_TypeDef *) (hjtaglm->Config->BaseAddr);

DONE:
	{
		return ret;
	}
}

/*************************************************************
 * @brief AXI-to-JTAG LM configuration, config interrupt and
 * calculate FREQ_WROD automaticlly
 *
 * @param hjtaglm : AXI JTAG LM Handler
 *
 * @retval : 0 for success, else for error code
 ************************************************************/
int AxiJtagLM_Config(AxiJtagLM_HandleTypeDef *hjtaglm)
{
	int ret = 0;

	if ((hjtaglm == NULL) || (hjtaglm->Config == NULL) || (hjtaglm->Instance == NULL))
	{
		ret = -1;
		goto DONE;
	}

	if ((hjtaglm->Init.TckFrequency == 0)
	        || (hjtaglm->Config->mJTAGClkFrequency == 0))
	{
		ret = -3;
		goto DONE;
	}

	//wait for last trans finished
	while(hjtaglm->Instance->CR & REG_CR_ENABLE_MASK)
		;

	hjtaglm->IsBusy = false;

	hjtaglm->Instance->FREQ_WORD = (uint64_t) hjtaglm->Init.TckFrequency
	                               * 0xFFFFFFFF / hjtaglm->Config->mJTAGClkFrequency;

	hjtaglm->Instance->CR =
	    (hjtaglm->Init.IntEn == true) ? REG_CR_INT_EN_MASK : 0;

DONE:
	{
		return ret;
	}
}

/*************************************************************
 * @brief AXI-to-JTAG LM Shifter, this function will be blocked
 * until Shifter finished
 *
 * @param hjtaglm : AXI-to-JTAG LM Handler
 * @param cmd : AXI-to-JTAG LM Command
 *
 * @retval : 0 for success, else for error code
 ************************************************************/
int AxiJtagLM_Shift(AxiJtagLM_HandleTypeDef *hjtaglm, AxiJtagLM_CmdTypeDef *cmd)
{
	int ret = 0;

	if ((hjtaglm == NULL) || (cmd == NULL))
	{
		ret = -1;
		goto DONE;
	}

	if(cmd->Length == 0)
	{
		ret = -3;
		goto DONE;
	}

	hjtaglm->IsBusy = true;
	hjtaglm->Cmd = *cmd;

	hjtaglm->Instance->LENGTH = cmd->Length;
	hjtaglm->Instance->TMS_MEM_ADDR = (uint32_t)cmd->TmsMemPtr;
	hjtaglm->Instance->TDI_MEM_ADDR = (uint32_t)cmd->TdiMemPtr;
	hjtaglm->Instance->TDO_MEM_ADDR = (uint32_t)cmd->TdoMemPtr;

	hjtaglm->Instance->CR |= REG_CR_ENABLE_MASK;

	while (hjtaglm->Instance->CR & REG_CR_ENABLE_MASK)
		;

	hjtaglm->IsBusy = false;

DONE:
	{
		return ret;
	}
}

/*************************************************************
 * @brief AXI-to-JTAG LM Asynchronous Shifter, this function
 * start JTAG shifter asynchronously
 *
 * @param hjtaglm : AXI-to-JTAG LM Handler
 * @param cmd : AXI-to-JTAG LM Command
 *
 * @retval : 0 for success, 1 for busy, else for error code
 ************************************************************/
int AxiJtagLM_Shift_Async(AxiJtagLM_HandleTypeDef *hjtaglm, AxiJtagLM_CmdTypeDef *cmd)
{
	int ret = 0;

	if ((hjtaglm == NULL) || (cmd == NULL))
	{
		ret = -1;
		goto DONE;
	}

	if (hjtaglm->Instance->CR & REG_CR_ENABLE_MASK)
	{
		ret = 1;
		goto DONE;
	}

	if(cmd->Length == 0)
	{
		ret = -3;
		goto DONE;
	}

	hjtaglm->IsBusy = true;
	hjtaglm->Cmd = *cmd;

	hjtaglm->Instance->LENGTH = cmd->Length;
	hjtaglm->Instance->TMS_MEM_ADDR = (uint32_t)cmd->TmsMemPtr;
	hjtaglm->Instance->TDI_MEM_ADDR = (uint32_t)cmd->TdiMemPtr;
	hjtaglm->Instance->TDO_MEM_ADDR = (uint32_t)cmd->TdoMemPtr;

	hjtaglm->Instance->CR |= REG_CR_ENABLE_MASK;

DONE:
	{
		return ret;
	}
}

/*************************************************************
 * @brief AXI-to-JTAG LM Shifter Interrupt Callback function
 *
 * @param hjtaglm : AXI-to-JTAG LM Handler
 *
 * @retval : 0 for success, else for error code
 ************************************************************/
int AxiJtagLM_Callback(AxiJtagLM_HandleTypeDef *hjtaglm)
{
	int ret = 0;

	if (hjtaglm == NULL)
	{
		ret = -1;
		goto DONE;
	}

	//Interrupt clear
	hjtaglm->Instance->CR &= ~REG_CR_INT_STATUS_MASK;

	hjtaglm->IsBusy = false;

DONE:
	{
		return ret;
	}
}